/**
 * 
 */
package com.miolr.module.core.service;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;

import javax.inject.Inject;
import javax.inject.Named;

import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Restrictions;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.security.crypto.encrypt.TextEncryptor;
import org.springframework.social.connect.Connection;
import org.springframework.social.connect.ConnectionFactoryLocator;
import org.springframework.social.connect.ConnectionKey;
import org.springframework.social.connect.ConnectionRepository;
import org.springframework.social.connect.ConnectionSignUp;
import org.springframework.social.connect.UsersConnectionRepository;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.MultiValueMap;

import com.miolr.core.service.local.LocalGenericService;
import com.miolr.module.core.dao.SocialConnectionDao;
import com.miolr.module.core.model.SocialConnection;
import com.miolr.module.core.social.SocialUserConnectionRepository;

/**
 * @author iday
 * 
 */
@Named("usersConnectionRepository")
@Transactional(propagation = Propagation.REQUIRED, readOnly = true)
public class SocialConnectionService extends
		LocalGenericService<SocialConnectionDao, SocialConnection> implements
		UsersConnectionRepository, ConnectionSignUp, BeanFactoryAware {
	@Inject
	private ConnectionFactoryLocator connectionFactoryLocator;
	@Inject
	private TextEncryptor textEncryptor;
	private BeanFactory beanFactory;

	@Override
	public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
		this.beanFactory = beanFactory;
	}

	/**
	 * @param connectionFactoryLocator
	 *            the connectionFactoryLocator to set
	 */
	public void setConnectionFactoryLocator(
			ConnectionFactoryLocator connectionFactoryLocator) {
		this.connectionFactoryLocator = connectionFactoryLocator;
	}

	/**
	 * @param textEncryptor
	 *            the textEncryptor to set
	 */
	public void setTextEncryptor(TextEncryptor textEncryptor) {
		this.textEncryptor = textEncryptor;
	}

	@Override
	public ConnectionRepository createConnectionRepository(String userId) {
		return new SocialUserConnectionRepository(
				this.beanFactory.getBean(SocialConnectionService.class),
				userId, connectionFactoryLocator, textEncryptor);
	}

	@Override
	public Set<String> findUserIdsConnectedTo(String providerId,
			Set<String> providerUserIds) {
		List<SocialConnection> list = this.dao.findConnectedTo(providerId,
				providerUserIds);
		final Set<String> localUserIds = new HashSet<String>();
		for (SocialConnection conn : list) {
			localUserIds.add(conn.getUserId());
		}
		return localUserIds;
	}

	@Override
	public List<String> findUserIdsWithConnection(Connection<?> connection) {
		ConnectionKey key = connection.getKey();
		List<SocialConnection> list = this.dao.findConnectedTo(
				key.getProviderId(), key.getProviderUserId());
		final List<String> localUserIds = new ArrayList<String>();
		if (!list.isEmpty()) {
			for (SocialConnection conn : list) {
				localUserIds.add(conn.getUserId());
			}
		}
		return localUserIds;
	}

	@Override
	@Transactional(propagation = Propagation.REQUIRED)
	public String execute(Connection<?> connection) {
		return connection.getDisplayName();
	}

	public List<SocialConnection> findByUserId(String userId) {
		return this.dao.findByUserId(userId);
	}

	public List<SocialConnection> findByUserId(String providerId, String userId) {
		return this.dao.findByUserId(providerId, userId);
	}

	public List<SocialConnection> findByProviderUsers(String userId,
			MultiValueMap<String, String> providerUsers) {
		DetachedCriteria criteria = DetachedCriteria
				.forClass(SocialConnection.class);
		for (Entry<String, List<String>> entry : providerUsers.entrySet()) {
			criteria.add(Restrictions.eq("providerId", entry.getKey()));
			criteria.add(Restrictions.in("providerUserId", entry.getValue()));
		}
		criteria.add(Restrictions.eq("userId", userId));
		return this.queryAll(criteria, null, true, null);
	}

	public SocialConnection findOne(String providerId, String providerUserId,
			String userId) {
		return dao.find(providerId, providerUserId, userId);
	}

	public SocialConnection findPrimary(String providerId, String userId) {
		return dao.findPrimary(providerId, userId);
	}

}
